/*
 * (c) Copyright IBM Corp. 2000, 2001.
 * All Rights Reserved.
 */
package org.epic.perleditor.templates.textmanipulation;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;


//import net.sourceforge.phpdt.internal.corext.util.IOCloser;
import org.epic.perleditor.templates.util.IOCloser;
//import net.sourceforge.phpdt.internal.ui.PHPStatusConstants;
import org.epic.perleditor.templates.ui.EPICStatusConstants;
//import net.sourceforge.phpeclipse.PHPeclipsePlugin;
import org.epic.perleditor.PerlEditorPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;

/* package */ class TextBufferFactory {

    private IDocumentProvider fDocumentProvider;
    private Map<FileEditorInput, Value> fFileValueMap;
    private Map<TextBuffer, Value> fBufferValueMap;
    
    private static class Value {
        TextBuffer buffer;
        FileEditorInput input;
        IDocument document;
        IAnnotationModel annotationModel;
        int references;
        public Value(TextBuffer b, FileEditorInput i, IDocument d, IAnnotationModel m) {
            buffer= b;
            input= i;
            document= d;
            annotationModel= m;
        }
    }

    public TextBufferFactory() {
        // XXX http://dev.eclipse.org/bugs/show_bug.cgi?id=5170
        // Need way to map a file to a document without knowing any kind of document provider.
        this(PerlEditorPlugin.getDefault().getDocumentProvider());
    }
    
    public TextBufferFactory(IDocumentProvider provider) {
        fDocumentProvider= provider;
        Assert.isNotNull(fDocumentProvider);
        fFileValueMap= new HashMap<FileEditorInput, Value>(5);
        fBufferValueMap= new HashMap<TextBuffer, Value>(5);
    }

    public TextBuffer acquire(IFile file) throws CoreException {
        FileEditorInput input= new FileEditorInput(file);	
        
        Value value= fFileValueMap.get(input);
        if (value != null) {
            value.references++;
            return value.buffer;
        }
        
        fDocumentProvider.connect(input);
        IDocument document= fDocumentProvider.getDocument(input);
        IAnnotationModel annotationModel= fDocumentProvider.getAnnotationModel(input);
        annotationModel.connect(document);
        value= new Value(new TextBuffer(document), input, document, annotationModel);
        fFileValueMap.put(input, value);
        fBufferValueMap.put(value.buffer, value);
        value.references++;
        return value.buffer;
    }
    
    public void release(TextBuffer buffer) {
        final Value value= fBufferValueMap.get(buffer);
        if (value == null)
            return;
                        
        value.references--;
        if (value.references == 0) {
            buffer.release();	
            value.annotationModel.disconnect(value.document);
            fDocumentProvider.disconnect(value.input);
            fFileValueMap.remove(value.input);
            fBufferValueMap.remove(buffer);
        }
    }
    
    public void commitChanges(TextBuffer buffer, boolean force, IProgressMonitor pm) throws CoreException {
        final Value value= fBufferValueMap.get(buffer);
        if (value == null)
            return;
        
        boolean save= force || fDocumentProvider.mustSaveDocument(value.input);
        if (save) {
            IWorkspaceRunnable action= new IWorkspaceRunnable() {
                public void run(IProgressMonitor pm) throws CoreException {
                    fDocumentProvider.aboutToChange(value.input);
                    fDocumentProvider.saveDocument(pm, value.input, value.document, true);
                }
            };
            try {
                ResourcesPlugin.getWorkspace().run(action, pm);
            } finally {
                fDocumentProvider.changed(value.input);
            }
        }
    }
    
    public TextBuffer create(IFile file) throws CoreException {
        FileEditorInput input= new FileEditorInput(file);	
        IDocument document= fDocumentProvider.getDocument(input);
        if (document != null) {
            return new TextBuffer(new Document(document.get()));
        } else {
            return createFromFile(file);
        }
    }

    private TextBuffer createFromFile(IFile file) throws CoreException {
        IDocument document;
        // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=19319
        InputStream stream= file.getContents();
        InputStreamReader in= null;
        try {		
            document= new Document();
            in= new InputStreamReader(new BufferedInputStream(stream), ResourcesPlugin.getEncoding());
            StringBuilder buffer= new StringBuilder();
            char[] readBuffer= new char[2048];
            int n= in.read(readBuffer);
            while (n > 0) {
                buffer.append(readBuffer, 0, n);
                n= in.read(readBuffer);
            }
            document.set(buffer.toString());
            return new TextBuffer(document);
        } catch (IOException x) {
            IStatus s= new Status(IStatus.ERROR, PerlEditorPlugin.getPluginId(), EPICStatusConstants.INTERNAL_ERROR, x.getMessage(), x);
            throw new CoreException(s);
        } finally {
            IOCloser.perform(in, stream);
        }
    }
    
    public TextBuffer create(String content) {
        return new TextBuffer(new Document(content));
    }
    
    public void save(TextBuffer buffer, IProgressMonitor pm) throws CoreException {
        Value value= fBufferValueMap.get(buffer);
        if (value == null)
            throwNotManaged();
        fDocumentProvider.saveDocument(pm, value.input, value.document, true);
    }

    public void aboutToChange(TextBuffer buffer) throws CoreException {
        Value value= fBufferValueMap.get(buffer);
        if (value == null)
            throwNotManaged();
        fDocumentProvider.aboutToChange(value.input);
    }
        
    public void changed(TextBuffer buffer) throws CoreException {
        Value value= fBufferValueMap.get(buffer);
        if (value == null)
            throwNotManaged();
        fDocumentProvider.changed(value.input);
    }
    
    private void throwNotManaged() throws CoreException {
        IStatus s= new Status(IStatus.ERROR, PerlEditorPlugin.getPluginId(), 
            EPICStatusConstants.INTERNAL_ERROR, TextManipulationMessages.getString("TextBufferFactory.bufferNotManaged"), null); //$NON-NLS-1$
        throw new CoreException(s);
    }
}

